{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b46bdfce-8558-4db7-bf6c-e30409103a0c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "device(type='cuda')"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from torchvision import transforms, datasets\n",
    "from torch.utils.data import DataLoader\n",
    "import matplotlib.pyplot as plt\n",
    "from tqdm import tqdm\n",
    "#隐藏警告\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")               #忽略警告信息\n",
    "plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号\n",
    "plt.rcParams['figure.dpi']         = 100        #分辨率\n",
    "\n",
    "\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "device"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb3202bf-034c-4a4e-8a39-89925e0007c5",
   "metadata": {},
   "source": [
    "### 读取（下载）MNIST数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5cc5c44b-a5f0-422d-bde6-d4d1c984f278",
   "metadata": {},
   "outputs": [],
   "source": [
    "transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.5,), (0.5,))  # 归一化到[-1, 1]，更适合tanh激活函数\n",
    "])\n",
    "\n",
    "\"\"\"\n",
    "num_workers=4 : 使用 4 个子进程来并行加载数据，这可以加快数据加载速度\n",
    "pin_memory=True : 将加载的数据张量固定在内存, 数据加载到 GPU 的速度会更快, 通常在使用 GPU 训练时，将此参数设置为 True\n",
    "\"\"\"\n",
    "train_dataset = datasets.MNIST(root='../datasets/mnist', train=True, download=True, transform=transform)  # download=True:如果没有, 下载数据集\n",
    "test_dataset = datasets.MNIST(root='../datasets/mnist', train=False, download=True, transform=transform)  # train=True训练集，=False测试集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "12857fef-94c9-476a-b5e8-38b5a2b4105f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 增大batch_size以提高稳定性\n",
    "batch_size = 64\n",
    "\n",
    "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4, pin_memory=True)\n",
    "test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=4, pin_memory=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b077b1f0-4933-4603-9de1-1ca9bdd53678",
   "metadata": {},
   "source": [
    "### 构建模型\n",
    "**Generator 和 Discriminator**\n",
    "\n",
    "[反卷积(Transposed conv deconv)实现原理（通俗易懂）](https://blog.csdn.net/weixin_39326879/article/details/120797857) : 在GAN中，生成器使用反卷积层将低维随机噪声转换为高分辨率图像"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0cb638e1-16ba-49d2-9df3-d988baad2961",
   "metadata": {},
   "source": [
    "#### 反卷积层（Deconvolution Layer）\n",
    "\n",
    "反卷积层，也称为**转置卷积层（Transposed Convolution Layer）**，是一种用于上采样的操作。它的作用是将低分辨率的特征图（feature map）转换为高分辨率的特征图。反卷积层在生成对抗网络（GAN）、图像分割、超分辨率等任务中非常常见。\n",
    "\n",
    "\n",
    "| 特性                | 卷积层（Convolution）                          | 反卷积层（Transposed Convolution）            |\n",
    "|---------------------|-----------------------------------------------|-----------------------------------------------|\n",
    "| **目的**            | 下采样，提取特征                              | 上采样，生成高分辨率特征图                    |\n",
    "| **输入与输出关系**  | 输入尺寸 > 输出尺寸                           | 输入尺寸 < 输出尺寸                           |\n",
    "| **计算方式**        | 通过滑动窗口和卷积核计算输出                  | 通过填充和卷积核的转置计算输出                |\n",
    "| **应用场景**        | 特征提取、分类、检测等                        | 图像生成、分割、超分辨率等                    |"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "198d2b1d-7911-4d8c-bdad-c95b35e44396",
   "metadata": {},
   "source": [
    "#### Generator \n",
    "生成器的目标是从随机噪声（latent vector）和条件（condition）生成逼真的图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ee6ff0a6-5c40-40a0-9f45-4416398709c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Generator(nn.Module):\n",
    "    def __init__(self, input_dim=100, output_dim=1, class_num=10):\n",
    "        '''\n",
    "        初始化生成网络\n",
    "        :param input_dim:输入随机噪声的维度，（随机噪声是为了增加输出多样性）\n",
    "        :param output_dim:生成图像的通道数（灰度图为1，RGB图为3）\n",
    "        :param class_num:图像种类\n",
    "        '''\n",
    "        super(Generator, self).__init__()\n",
    "        \"\"\"\n",
    "         为什么需要拼接随机噪声和条件向量？\n",
    "         拼接随机噪声和条件向量的目的是将两种信息结合起来，作为生成器的输入：\n",
    "         随机噪声：提供生成数据的随机性。\n",
    "         条件向量：提供生成数据的条件信息。\n",
    "         通过拼接，生成器可以根据条件向量生成符合特定条件的数据, 同时确保每次生成的数据会有所不同\n",
    "         \"\"\"\n",
    "        self.input_dim = input_dim\n",
    "        self.class_num = class_num\n",
    "        self.output_dim = output_dim\n",
    "        \n",
    "        # 嵌入层处理条件向量(类别标签), 提高条件信息的表达能力\n",
    "        self.label_emb = nn.Embedding(class_num, class_num)\n",
    "        \n",
    "        # 全连接层，将输入向量映射到高维空间，然后通过反卷积层生成图像\n",
    "        self.fc = nn.Sequential(\n",
    "            nn.Linear(self.input_dim + self.class_num, 256),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Linear(256, 512),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Linear(512, 1024),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Linear(1024, 128 * 7 * 7),\n",
    "            nn.BatchNorm1d(128 * 7 * 7),\n",
    "            nn.LeakyReLU(0.2, inplace=True)\n",
    "        )\n",
    "\n",
    "        # 反卷积层（转置卷积层），用于将高维特征图逐步上采样为最终图像\n",
    "        self.deconv = nn.Sequential(\n",
    "            nn.ConvTranspose2d(128, 128, 4, 2, 1),  # 7x7 -> 14x14\n",
    "            nn.BatchNorm2d(128),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.ConvTranspose2d(128, 64, 4, 2, 1),   # 14x14 -> 28x28\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Conv2d(64, self.output_dim, 3, 1, 1),  # 保持尺寸不变，但细化特征\n",
    "            nn.Tanh()  # 激活函数，将输出值限制在 [-1, 1] 范围内，适合生成图像\n",
    "        )\n",
    " \n",
    "    def forward(self, noise, labels):\n",
    "        # 标签处理\n",
    "        label_embedding = self.label_emb(labels)\n",
    "        \n",
    "        # 拼接噪声和条件向量\n",
    "        x = torch.cat([noise, label_embedding], dim=1)\n",
    "        \n",
    "        # 通过全连接层\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        # 重塑为特征图\n",
    "        x = x.view(-1, 128, 7, 7)\n",
    "        \n",
    "        # 通过反卷积层生成图像\n",
    "        x = self.deconv(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "532a2f68-9e49-45bd-9419-c483c1bac15e",
   "metadata": {},
   "source": [
    "#### Discriminator \n",
    "判别器的目标是区分输入图像是真实的还是生成的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a66459d5-8040-44d1-9258-edf98b3a29ca",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn.utils.spectral_norm as spectral_norm\n",
    "\n",
    "class Discriminator(nn.Module):\n",
    "    def __init__(self, input_dim=1, output_dim=1, class_num=10):\n",
    "        '''\n",
    "        初始化判别网络\n",
    "        :param input_dim:输入通道数\n",
    "        :param output_dim:输出通道数\n",
    "        '''\n",
    "        super(Discriminator, self).__init__()\n",
    "        self.input_dim = input_dim\n",
    "        self.output_dim = output_dim\n",
    "        self.class_num = class_num\n",
    "\n",
    "        # 标签嵌入\n",
    "        self.label_emb = nn.Embedding(class_num, 28*28)\n",
    "\n",
    "        # 卷积层使用频谱归一化提高稳定性\n",
    "        self.conv = nn.Sequential(\n",
    "            nn.Conv2d(self.input_dim + 1, 64, 4, 2, 1),  # +1 是为了连接条件信息\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),  # 添加Dropout防止过拟合\n",
    "            \n",
    "            nn.utils.spectral_norm(nn.Conv2d(64, 128, 4, 2, 1)),  # 频谱归一化\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),\n",
    "            \n",
    "            nn.utils.spectral_norm(nn.Conv2d(128, 256, 3, 1, 1)),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),\n",
    "        )\n",
    "        \n",
    "        # 全连接层\n",
    "        self.fc = nn.Sequential(\n",
    "            nn.utils.spectral_norm(nn.Linear(256 * 7 * 7, 1024)),\n",
    "            nn.LeakyReLU(0.2, inplace=True),\n",
    "            nn.Dropout(0.3),\n",
    "            nn.utils.spectral_norm(nn.Linear(1024, self.output_dim))\n",
    "        )\n",
    " \n",
    "    def forward(self, img, labels):\n",
    "        # 处理标签\n",
    "        batch_size, _, height, width = img.shape\n",
    "        label_embedding = self.label_emb(labels).view(batch_size, 1, height, width)\n",
    "        \n",
    "        # 连接图像和标签信息\n",
    "        x = torch.cat([img, label_embedding], dim=1)\n",
    "        \n",
    "        # 卷积层\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 全连接层\n",
    "        x = x.view(batch_size, -1)\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17dcd94d-780b-491a-b254-1bb6e2b3b64a",
   "metadata": {},
   "source": [
    "### 模型训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7f876252-f84c-4843-bfba-115e97170115",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 初始化权重函数\n",
    "def weights_init(m):\n",
    "    classname = m.__class__.__name__\n",
    "    if classname.find('Conv') != -1:\n",
    "        nn.init.normal_(m.weight.data, 0.0, 0.02)\n",
    "    elif classname.find('BatchNorm') != -1:\n",
    "        nn.init.normal_(m.weight.data, 1.0, 0.02)\n",
    "        nn.init.constant_(m.bias.data, 0)\n",
    "    elif classname.find('Linear') != -1:\n",
    "        nn.init.normal_(m.weight.data, 0.0, 0.02)\n",
    "        nn.init.constant_(m.bias.data, 0)\n",
    "\n",
    "# 定义生成器和判别器\n",
    "generator = Generator(input_dim=100, output_dim=1, class_num=10).to(device)\n",
    "discriminator = Discriminator(input_dim=1, output_dim=1, class_num=10).to(device)\n",
    "\n",
    "# 应用权重初始化\n",
    "generator.apply(weights_init)\n",
    "discriminator.apply(weights_init)\n",
    "\n",
    "# 使用Adam优化器，beta参数适合GAN训练\n",
    "optimizer_G = torch.optim.Adam(generator.parameters(), lr=0.0001, betas=(0.5, 0.999))\n",
    "optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))\n",
    "\n",
    "\n",
    "# 使用Wasserstein GAN with Gradient Penalty (WGAN-GP)损失函数\n",
    "def compute_gradient_penalty(D, real_samples, fake_samples, labels):\n",
    "    \"\"\"计算梯度惩罚项\"\"\"\n",
    "    # 随机权重项\n",
    "    alpha = torch.rand(real_samples.size(0), 1, 1, 1).to(device)\n",
    "    \n",
    "    # 在真实样本和生成样本之间进行插值\n",
    "    interpolates = (alpha * real_samples + ((1 - alpha) * fake_samples)).requires_grad_(True)\n",
    "    \n",
    "    # 计算插值样本的判别器输出\n",
    "    d_interpolates = D(interpolates, labels)\n",
    "    \n",
    "    # 获取梯度\n",
    "    gradients = torch.autograd.grad(\n",
    "        outputs=d_interpolates,\n",
    "        inputs=interpolates,\n",
    "        grad_outputs=torch.ones_like(d_interpolates),\n",
    "        create_graph=True,\n",
    "        retain_graph=True,\n",
    "        only_inputs=True\n",
    "    )[0]\n",
    "    \n",
    "    # 梯度惩罚\n",
    "    gradients = gradients.view(gradients.size(0), -1)\n",
    "    gradient_penalty = ((gradients.norm(2, dim=1) - 1) ** 2).mean()\n",
    "    \n",
    "    return gradient_penalty"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "338ab99f-9789-483d-ab7e-4b7926c4fbaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(dataloader, generator, discriminator, optimizer_G, optimizer_D, epoch):\n",
    "    generator.train()\n",
    "    discriminator.train()\n",
    "    running_loss_G = 0.0\n",
    "    running_loss_D = 0.0\n",
    "    \n",
    "    lambda_gp = 10  # 梯度惩罚权重\n",
    "    \n",
    "    progress_bar = tqdm(dataloader, desc=\"Training\", leave=False)\n",
    "    for index, (images, labels) in enumerate(progress_bar):\n",
    "        # 将数据移动到设备\n",
    "        batch_size = images.size(0)\n",
    "        images = images.to(device)\n",
    "        labels = labels.to(device)\n",
    "        \n",
    "        # ---------------------\n",
    "        #  训练判别器\n",
    "        # ---------------------\n",
    "        optimizer_D.zero_grad()\n",
    "        \n",
    "        # 生成随机噪声\n",
    "        z = torch.randn(batch_size, generator.input_dim).to(device)\n",
    "        \n",
    "        # 生成假图像\n",
    "        fake_images = generator(z, labels)\n",
    "        \n",
    "        # 计算真实和假图像的判别器输出\n",
    "        real_validity = discriminator(images, labels)\n",
    "        fake_validity = discriminator(fake_images.detach(), labels)\n",
    "        \n",
    "        # 梯度惩罚项\n",
    "        gradient_penalty = compute_gradient_penalty(\n",
    "            discriminator, images.data, fake_images.data, labels\n",
    "        )\n",
    "        \n",
    "        # 判别器损失 (WGAN-GP)\n",
    "        d_loss = -torch.mean(real_validity) + torch.mean(fake_validity) + lambda_gp * gradient_penalty\n",
    "        \n",
    "        # 更新判别器\n",
    "        d_loss.backward()\n",
    "        optimizer_D.step()\n",
    "        \n",
    "        running_loss_D += d_loss.item()\n",
    "        \n",
    "        # ---------------------\n",
    "        #  训练生成器\n",
    "        # ---------------------\n",
    "        # 每n_critic次更新一次生成器\n",
    "        n_critic = 5\n",
    "        if index % n_critic == 0:\n",
    "            optimizer_G.zero_grad()\n",
    "            \n",
    "            # 生成假图像\n",
    "            fake_images = generator(z, labels)\n",
    "            \n",
    "            # 判别器对假图像的评估\n",
    "            fake_validity = discriminator(fake_images, labels)\n",
    "            \n",
    "            # 生成器损失\n",
    "            g_loss = -torch.mean(fake_validity)\n",
    "            \n",
    "            # 更新生成器\n",
    "            g_loss.backward()\n",
    "            optimizer_G.step()\n",
    "            \n",
    "            running_loss_G += g_loss.item()\n",
    "            \n",
    "            # 更新进度条\n",
    "            progress_bar.set_postfix(\n",
    "                epoch=epoch+1,\n",
    "                G_loss=g_loss.item(),\n",
    "                D_loss=d_loss.item()\n",
    "            )\n",
    "        \n",
    "    avg_loss_G = running_loss_G / (len(dataloader) // n_critic)\n",
    "    avg_loss_D = running_loss_D / len(dataloader)\n",
    "    \n",
    "    return avg_loss_G, avg_loss_D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c9a1c0ac-5c59-4f2f-96a2-41bbff0f7e15",
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate(dataloader, generator, discriminator, epoch):\n",
    "    generator.eval()\n",
    "    discriminator.eval()\n",
    "    running_loss_G = 0.0\n",
    "    running_loss_D = 0.0\n",
    "    \n",
    "    lambda_gp = 10  # 梯度惩罚权重\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        progress_bar = tqdm(dataloader, desc=\"Evaluating\", leave=True)\n",
    "        for images, labels in progress_bar:\n",
    "            # 将数据移动到设备\n",
    "            batch_size = images.size(0)\n",
    "            images = images.to(device)\n",
    "            labels = labels.to(device)\n",
    "            \n",
    "            # 生成随机噪声\n",
    "            z = torch.randn(batch_size, generator.input_dim).to(device)\n",
    "            \n",
    "            # 生成假图像\n",
    "            fake_images = generator(z, labels)\n",
    "            \n",
    "            # 计算真实和假图像的判别器输出\n",
    "            real_validity = discriminator(images, labels)\n",
    "            fake_validity = discriminator(fake_images, labels)\n",
    "            \n",
    "            # 判别器损失 (WGAN-GP)\n",
    "            d_loss = -torch.mean(real_validity) + torch.mean(fake_validity)\n",
    "            \n",
    "            # 生成器损失\n",
    "            g_loss = -torch.mean(fake_validity)\n",
    "            \n",
    "            # 统计损失\n",
    "            running_loss_G += g_loss.item()\n",
    "            running_loss_D += d_loss.item()\n",
    "            \n",
    "            # 更新进度条\n",
    "            progress_bar.set_postfix(\n",
    "                epoch=epoch+1,\n",
    "                G_loss=g_loss.item(),\n",
    "                D_loss=d_loss.item()\n",
    "            )\n",
    "    \n",
    "    avg_loss_G = running_loss_G / len(dataloader)\n",
    "    avg_loss_D = running_loss_D / len(dataloader)\n",
    "    \n",
    "    return avg_loss_G, avg_loss_D"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "3d14364b-5bfe-421b-84d6-55c7d54e71f7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 训练过程中生成图片方便查看训练效果\n",
    "def generate_images(generator, epoch, fixed_noise=None, fixed_labels=None):\n",
    "    generator.eval()\n",
    "    with torch.no_grad():\n",
    "        num_images = 100\n",
    "        num_classes = 10\n",
    "        \n",
    "        # 使用固定噪声和标签以便比较不同epoch的进展\n",
    "        if fixed_noise is None:\n",
    "            fixed_noise = torch.randn(num_images, generator.input_dim).to(device)\n",
    "        if fixed_labels is None:\n",
    "            # 每个数字类别生成10张图片\n",
    "            fixed_labels = torch.tensor([i for i in range(num_classes) for _ in range(num_images // num_classes)]).to(device)\n",
    "        \n",
    "        # 生成图片\n",
    "        fake_images = generator(fixed_noise, fixed_labels)\n",
    "        fake_images = fake_images.cpu().detach()\n",
    "        \n",
    "        # 可视化并保存图片\n",
    "        fig, axes = plt.subplots(10, 10, figsize=(10, 10))\n",
    "        fig.subplots_adjust(hspace=0.1, wspace=0.1)\n",
    "        \n",
    "        for i, ax in enumerate(axes.flat):\n",
    "            # 转换图像范围从[-1, 1]到[0, 1]\n",
    "            img = (fake_images[i].squeeze() + 1) / 2\n",
    "            ax.imshow(img, cmap='gray')\n",
    "            ax.axis('off')\n",
    "            \n",
    "            # 在每行的第一列显示类别标签\n",
    "            if i % 10 == 0:\n",
    "                ax.set_title(f'{fixed_labels[i].item()}', fontsize=8)\n",
    "        \n",
    "        plt.savefig(f'./data/epoch_{epoch}.png', bbox_inches='tight')\n",
    "        plt.close()\n",
    "        \n",
    "        return fixed_noise, fixed_labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d46b4ba4-bcbf-49c0-9c52-25b8ca7d4613",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 43.63it/s, D_loss=-2.22, G_loss=-27.4, epoch=1]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 1, Train_loss_G:-26.288, Train_loss_D:-5.394, Test_loss_G:-27.418, Test_loss_D:-2.361\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 157/157 [00:03<00:00, 44.97it/s, D_loss=-2.05, G_loss=-27, epoch=2]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 2, Train_loss_G:-26.964, Train_loss_D:-1.955, Test_loss_G:-26.983, Test_loss_D:-2.036\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 45.04it/s, D_loss=-1.11, G_loss=-26.1, epoch=3]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 3, Train_loss_G:-26.294, Train_loss_D:-1.687, Test_loss_G:-26.264, Test_loss_D:-1.135\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 45.21it/s, D_loss=-1.08, G_loss=-24.6, epoch=4]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 4, Train_loss_G:-25.024, Train_loss_D:-1.349, Test_loss_G:-24.472, Test_loss_D:-1.239\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:04<00:00, 39.15it/s, D_loss=-1.97, G_loss=-18.6, epoch=5]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 5, Train_loss_G:-24.045, Train_loss_D:-1.107, Test_loss_G:-18.988, Test_loss_D:-0.981\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 43.00it/s, D_loss=0.763, G_loss=-23.1, epoch=6]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 6, Train_loss_G:-23.608, Train_loss_D:-0.881, Test_loss_G:-23.193, Test_loss_D:-0.603\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 46.08it/s, D_loss=0.771, G_loss=-31.5, epoch=7]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 7, Train_loss_G:-23.448, Train_loss_D:-0.776, Test_loss_G:-31.300, Test_loss_D:-1.303\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 45.48it/s, D_loss=-3.23, G_loss=-16.1, epoch=8]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 8, Train_loss_G:-22.776, Train_loss_D:-0.641, Test_loss_G:-16.241, Test_loss_D:-0.995\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 45.15it/s, D_loss=-2.58, G_loss=-11.4, epoch=9]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch: 9, Train_loss_G:-19.192, Train_loss_D:-0.689, Test_loss_G:-11.518, Test_loss_D:-0.593\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 45.44it/s, D_loss=-0.701, G_loss=-21.9, epoch=10]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:10, Train_loss_G:-22.790, Train_loss_D:-0.571, Test_loss_G:-21.858, Test_loss_D:-0.434\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 46.50it/s, D_loss=1.83, G_loss=-22.8, epoch=11]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:11, Train_loss_G:-22.411, Train_loss_D:-0.530, Test_loss_G:-23.062, Test_loss_D:0.031\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 157/157 [00:03<00:00, 44.11it/s, D_loss=-1.05, G_loss=-26.1, epoch=12]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:12, Train_loss_G:-20.840, Train_loss_D:-0.486, Test_loss_G:-26.222, Test_loss_D:-0.515\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 46.30it/s, D_loss=1.07, G_loss=-23.8, epoch=13]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:13, Train_loss_G:-20.207, Train_loss_D:-0.455, Test_loss_G:-24.116, Test_loss_D:0.073\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 157/157 [00:03<00:00, 40.21it/s, D_loss=-2.34, G_loss=-22.3, epoch=14]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:14, Train_loss_G:-23.558, Train_loss_D:-0.366, Test_loss_G:-22.446, Test_loss_D:-1.256\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████████| 157/157 [00:03<00:00, 44.93it/s, D_loss=-1, G_loss=-26, epoch=15]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:15, Train_loss_G:-26.863, Train_loss_D:-0.354, Test_loss_G:-26.430, Test_loss_D:-0.595\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 157/157 [00:03<00:00, 45.08it/s, D_loss=0.427, G_loss=-28.6, epoch=16]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:16, Train_loss_G:-28.163, Train_loss_D:-0.291, Test_loss_G:-29.112, Test_loss_D:-0.498\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 45.52it/s, D_loss=1.09, G_loss=-25.2, epoch=17]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:17, Train_loss_G:-26.051, Train_loss_D:-0.310, Test_loss_G:-25.853, Test_loss_D:-0.029\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 46.18it/s, D_loss=-0.495, G_loss=-26.6, epoch=18]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:18, Train_loss_G:-25.601, Train_loss_D:-0.312, Test_loss_G:-26.650, Test_loss_D:-0.481\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 157/157 [00:03<00:00, 46.05it/s, D_loss=0.537, G_loss=-25.7, epoch=19]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:19, Train_loss_G:-26.758, Train_loss_D:-0.319, Test_loss_G:-25.817, Test_loss_D:-0.134\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 157/157 [00:03<00:00, 44.54it/s, D_loss=0.296, G_loss=-28.4, epoch=20]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:20, Train_loss_G:-26.027, Train_loss_D:-0.166, Test_loss_G:-28.436, Test_loss_D:-0.202\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|███████████████████████████████| 157/157 [00:03<00:00, 46.19it/s, D_loss=-2.7, G_loss=-26.3, epoch=21]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:21, Train_loss_G:-29.559, Train_loss_D:-0.193, Test_loss_G:-27.006, Test_loss_D:-0.637\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 45.38it/s, D_loss=0.0625, G_loss=-33.3, epoch=22]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:22, Train_loss_G:-29.100, Train_loss_D:-0.240, Test_loss_G:-33.450, Test_loss_D:-0.301\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 42.00it/s, D_loss=-0.161, G_loss=-29.4, epoch=23]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:23, Train_loss_G:-30.264, Train_loss_D:-0.183, Test_loss_G:-29.449, Test_loss_D:-0.198\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|██████████████████████████████| 157/157 [00:03<00:00, 44.99it/s, D_loss=0.694, G_loss=-30.8, epoch=24]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:24, Train_loss_G:-30.210, Train_loss_D:-0.157, Test_loss_G:-30.884, Test_loss_D:-0.305\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 45.18it/s, D_loss=-0.674, G_loss=-30.8, epoch=25]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:25, Train_loss_G:-31.574, Train_loss_D:-0.121, Test_loss_G:-31.046, Test_loss_D:-0.025\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 45.02it/s, D_loss=-0.295, G_loss=-33.8, epoch=26]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:26, Train_loss_G:-33.373, Train_loss_D:-0.067, Test_loss_G:-33.979, Test_loss_D:-0.142\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████| 157/157 [00:03<00:00, 46.73it/s, D_loss=-0.624, G_loss=-32.4, epoch=27]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:27, Train_loss_G:-33.526, Train_loss_D:-0.104, Test_loss_G:-32.634, Test_loss_D:-0.330\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|█████████████████████████████████| 157/157 [00:03<00:00, 45.52it/s, D_loss=0.05, G_loss=-31, epoch=28]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:28, Train_loss_G:-31.969, Train_loss_D:-0.037, Test_loss_G:-31.179, Test_loss_D:0.150\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████| 157/157 [00:03<00:00, 45.30it/s, D_loss=-0.0176, G_loss=-31.3, epoch=29]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:29, Train_loss_G:-32.167, Train_loss_D:0.026, Test_loss_G:-31.351, Test_loss_D:-0.090\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Evaluating: 100%|████████████████████████████████| 157/157 [00:03<00:00, 44.38it/s, D_loss=0.508, G_loss=-32, epoch=30]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:30, Train_loss_G:-31.688, Train_loss_D:-0.038, Test_loss_G:-32.037, Test_loss_D:0.044\n",
      "训练完成!\n"
     ]
    }
   ],
   "source": [
    "# 开始训练\n",
    "num_epochs = 30\n",
    "train_loss_G = []\n",
    "train_loss_D = []\n",
    "test_loss_G = []\n",
    "test_loss_D = []\n",
    "\n",
    "# 创建固定噪声和标签用于图像生成\n",
    "fixed_noise = torch.randn(100, generator.input_dim).to(device)\n",
    "fixed_labels = torch.tensor([i for i in range(10) for _ in range(10)]).to(device)\n",
    "\n",
    "# 学习率调度器\n",
    "scheduler_G = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_G, mode='min', factor=0.5, patience=5, verbose=True)\n",
    "scheduler_D = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_D, mode='min', factor=0.5, patience=5, verbose=True)\n",
    "\n",
    "# 训练循环\n",
    "for epoch in range(num_epochs):\n",
    "    # 训练\n",
    "    epoch_train_loss_G, epoch_train_loss_D = train(train_loader, generator, discriminator, optimizer_G, optimizer_D, epoch)\n",
    "    \n",
    "    # 在测试集上评估\n",
    "    epoch_test_loss_G, epoch_test_loss_D = evaluate(test_loader, generator, discriminator, epoch)\n",
    "    \n",
    "    # 学习率调度\n",
    "    scheduler_G.step(epoch_test_loss_G)\n",
    "    scheduler_D.step(epoch_test_loss_D)\n",
    "    \n",
    "    # 记录损失\n",
    "    train_loss_G.append(epoch_train_loss_G)\n",
    "    train_loss_D.append(epoch_train_loss_D)\n",
    "    test_loss_G.append(epoch_test_loss_G)\n",
    "    test_loss_D.append(epoch_test_loss_D)\n",
    "    \n",
    "    # 打印训练和测试结果\n",
    "    template = ('Epoch:{:2d}, Train_loss_G:{:.3f}, Train_loss_D:{:.3f}, Test_loss_G:{:.3f}, Test_loss_D:{:.3f}')\n",
    "    print(template.format(epoch+1, epoch_train_loss_G, epoch_train_loss_D, epoch_test_loss_G, epoch_test_loss_D))\n",
    "    \n",
    "    # 每 5 个 epoch 生成并保存图片\n",
    "    if (epoch + 1) % 5 == 0 or epoch == 0:\n",
    "        generate_images(generator, epoch + 1, fixed_noise, fixed_labels)\n",
    "\n",
    "\n",
    "print(\"训练完成!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0464f73e-cdf8-41ae-a3ff-5ca64fc8a295",
   "metadata": {},
   "source": [
    "### 结果可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "cfa7bec7-b7fe-4b38-bcab-d4cebf4800d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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0oVvv/2W559/nvr163P16tUSn6Mo93sfxf2bsLS0BChwopv76dChAxEREXmOu2vXLsNzesb4uypM+/btUalUhvMCREVFcf78eUMZfv75Z7Zs2cLQoUN5//33CQwMBGDz5s0AREZG8uKLL9KiRQvmzZvHtm3bmDRpUpnXeBVCCGOSWF/8Mkisz0tivcT6mkC6f4ty5+rqSlZWFhs2bKBBgwZs2LCB7777jq5du1Z20fKwsbFh3bp1TJ8+HRcXFxITE/H19S32F7Grqyugmwnz4Ycf5qeffmLlypXUrl272GV44YUX6N27N7Nnz2bw4MF88cUXREVFlfi9NGvWjLp16xIVFZVveQ1XV1cOHjzI9u3bSU9P57333iM8PJzs7OwSn6cw93sfxf2b8PDwoF69enzyySeMGTOGixcv0rlz52J11Xv66adZtmwZw4YNIyAggPj4eObOncvw4cNp3bq10d5rdnY2//zzT77HO3XqhJ+fHxMmTGDu3LlotVrq1q3Lm2++SZ06dXjuuecAyMjIYPbs2QA0btyYo0ePkpycbKhVd3R05OuvvyY9PZ3Ro0eTlJTE7t27y7XWXQghSkpivcR6ifUS6x9kklSLcjdq1CiOHj3K66+/jqmpKb1792bBggV88sknJCYm4uDgUNlFBGD06NG89tpr7Nu3j8TERBRFwdXVlV9//bVYFwVdu3bl7bffZsWKFXz66ad06dKFDz74gJdeeonQ0FD8/Pzue4xevXrx9ddf8/bbb7NmzRoGDhzIiBEjSvV+Hn30UX799Vfat2+f5/G3336bqKgoRo4cibu7O08++SS1a9fm4MGDpTpPQe73PkryN/Hdd98xbdo0Pv/8c9zc3Pjjjz+KVQZLS0t2797NzJkzefrpp7G0tOSZZ55h6dKlRnufAHfu3ClwXdDjx4/Tvn171qxZg5ubG6+//jopKSn07duXX375BXt7e0B3QXD9+nWWLl1KREQErq6uLFiwgGeffRYAW1tbtm/fzquvvsrjjz8OQPfu3fn000+N+j6EEKIsJNZLrJdYL7H+QaZScg84EOIBdenSJZo3b84XX3xB/fr1UalUREVF8dJLLzFs2DCWL19e2UUUQgghRBlIrBdClBdJqoVA163ntdde45dffuH69etkZ2dTu3Zt+vbtyzvvvIObm1tlF1EIIYQQZSCxXghRXiSpFkII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",
      "text/plain": [
       "<Figure size 1200x300 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "epochs_range = range(num_epochs)\n",
    "\n",
    "plt.figure(figsize=(12, 3))\n",
    "\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.plot(epochs_range, train_loss_G, label='Training Generator Loss')\n",
    "plt.plot(epochs_range, train_loss_D, label='Training Discriminator Loss')\n",
    "plt.legend(loc='upper right')\n",
    "plt.title('Training and Validation Loss')\n",
    "\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.plot(epochs_range, test_loss_G, label='Test Generator Loss')\n",
    "plt.plot(epochs_range, test_loss_D, label='Test Discriminator Loss')\n",
    "plt.legend(loc='upper right')\n",
    "plt.title('Training and Validation Loss')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bf0e77fd-589a-4752-afde-a386b264e73a",
   "metadata": {},
   "source": [
    "### 模型保存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "14d399db-c56a-446c-b8e9-19baadbcf4ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 指定保存路径\n",
    "save_dir = '../models/4_GAN_Image_Generator'\n",
    "\n",
    "# 确保目录存在，如果不存在则创建\n",
    "import os\n",
    "if not os.path.exists(save_dir):\n",
    "    os.makedirs(save_dir)\n",
    "\n",
    "# 保存模型\n",
    "torch.save(generator.state_dict(), os.path.join(save_dir, 'MINIST_generator.pth'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e13bdb3-1256-4f9f-a1a5-fd6265d9a808",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.20"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
